package com.lambkit.sms.huawei;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.lambkit.core.Attr;
import com.lambkit.core.Lambkit;
import com.lambkit.sms.ISms;
import com.lambkit.util.Printer;

import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

public class HuaweiSms implements ISms {
	//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
    private final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
    private final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";

    private final String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";

    @Override
    public int sendVerifyLoginSMS(String to) {
        String cacheVerifyCode = Lambkit.getCache().get("SMS_CACHE_PHONE", to);
        if(StrUtil.isNotBlank(cacheVerifyCode)) {
            return 10501;
        }
        // 生成六位验证码
        String code = vcode();
        // 将生成的六位验证码和传进来的手机号码存入缓存，时间5分钟
        Lambkit.getCache().put("SMS_CACHE_PHONE", to, code, 300);
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(code);
        try {
            this.send(to, jsonArray.toJSONString());
            return 200;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public int sendNoticeSMS(String to, String notice) {
        try {
            this.send(to, notice);
            return 200;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public void send(String receiver, String templateParas) throws Exception {
    	HuaweiSmsConfig config = Lambkit.config(HuaweiSmsConfig.class);
    	send(config, receiver, templateParas);
    }

    /***
     *  发送短信
     * @param smsEnum 消息模板
     * @param phoneNumbers 接收者手机号
     * @param params 内容
     */
    public void send(HuaweiSmsConfig smsEnum, List<String> phoneNumbers, JSONArray params) throws Exception {
        send(smsEnum.getAppKey(),smsEnum.getAppSecret(),smsEnum.getSender(),String.join(",", phoneNumbers),smsEnum.getTemplateId(),params.toJSONString(),smsEnum.getSignature());
    }

    /***
     *  发送短信
      * @param smsEnum 消息模板
     * @param receiver 接收者手机号 + 86
     * @param templateParas 内容
     */
    public void send(HuaweiSmsConfig smsEnum, String receiver, String templateParas) throws Exception {
       send(smsEnum.getAppKey(),smsEnum.getAppSecret(),smsEnum.getSender(),receiver,smsEnum.getTemplateId(),templateParas,smsEnum.getSignature());
    }

    public void send(String appKey, String appSecret, String sender, String receiver, String templateId, String templateParas, String signature) throws Exception {
       send(appKey,appSecret,sender,receiver,templateId,templateParas,"",signature);
    }

    /***
     *
     * @param sender 通道好
     * @param receiver 接受者手机号 +86
     * @param templateId 模板id
     * @param templateParas 模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
     * @param statusCallBack 回调地址
     * @param signature 签名名称
     */
    public void send(String appKey, String appSecret, String sender, String receiver, String templateId, String templateParas, String statusCallBack, String signature) throws Exception {
        //请求Body,不携带签名名称时,signature请填null
        String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
        if (null == body || body.isEmpty()) {
        	if(Lambkit.context().isDevMode()) {
        		Printer.print(this, "sms", "body is null.");
        	}
            return;
        }
        //请求Headers中的X-WSSE参数值
        String wsseHeader = buildWsseHeader(appKey, appSecret);
        if (null == wsseHeader || wsseHeader.isEmpty()) {
        	if(Lambkit.context().isDevMode()) {
        		Printer.print(this, "sms", "wsse header is null.");
        	}
            return;
        }

        Writer out = null;
        BufferedReader in = null;
        StringBuffer result = new StringBuffer();
        HttpsURLConnection connection = null;
        InputStream is = null;
        HostnameVerifier hv = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        try {
            URL realUrl = new URL(url);
            connection = (HttpsURLConnection) realUrl.openConnection();

            connection.setHostnameVerifier(hv);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(true);
            //请求方法
            connection.setRequestMethod("POST");
            //请求Headers参数
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Authorization", AUTH_HEADER_VALUE);
            connection.setRequestProperty("X-WSSE", wsseHeader);

            connection.connect();
            out = new OutputStreamWriter(connection.getOutputStream());
            out.write(body); //发送请求Body参数
            out.flush();
            out.close();

            int status = connection.getResponseCode();
            if (200 == status) { //200
                is = connection.getInputStream();
            } else { //400/401
                is = connection.getErrorStream();
            }
            in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String line = "";
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            if(Lambkit.context().isDevMode()) {
            	Printer.print(this, "sms", result.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.close();
                }
                if (null != is) {
                    is.close();
                }
                if (null != in) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return;
    }

    /**
     * 构造请求Body体
     * @param sender
     * @param receiver
     * @param templateId
     * @param templateParas
     * @param statusCallBack
     * @param signature | 签名名称,使用国内短信通用模板时填写
     * @return
     */
    String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String statusCallBack, String signature) {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
        	if(Lambkit.context().isDevMode()) {
        		Printer.print(this, "sms", "buildRequestBody(): sender, receiver or templateId is null.");
        	}
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();

        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && !templateParas.isEmpty()) {
            map.put("templateParas", templateParas);
        }
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            map.put("statusCallback", statusCallBack);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }

        StringBuilder sb = new StringBuilder();
        String temp = "";

        for (String s : map.keySet()) {
            try {
                temp = URLEncoder.encode(map.get(s), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sb.append(s).append("=").append(temp).append("&");
        }

        return sb.deleteCharAt(sb.length()-1).toString();
    }

    /**
     * 构造X-WSSE参数值
     * @param appKey
     * @param appSecret
     * @return
     */
    String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
        	if(Lambkit.context().isDevMode()) {
        		Printer.print(this, "sms", "buildWsseHeader(): appKey or appSecret is null.");
        	}
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date()); //Created
        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce

        MessageDigest md;
        byte[] passwordDigest = null;

        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        //如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }

    /*** @throws Exception
     */
    void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[] {
                new X509TrustManager() {
                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return;
                    }
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return;
                    }
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                }
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }


}
